home *** CD-ROM | disk | FTP | other *** search
/ Apple Developer Connection Student Program / ADC Tools Sampler CD Disk 3 1999.iso / Metrowerks CodeWarrior / Java Support / Java_Source / Java2 / src / javax / swing / KeyboardManager.java < prev    next >
Encoding:
Java Source  |  1999-05-28  |  11.8 KB  |  355 lines  |  [TEXT/CWIE]

  1. /*
  2.  * @(#)JComponent.java    2.81 98/07/17
  3.  *
  4.  * Copyright 1997, 1998 by Sun Microsystems, Inc.,
  5.  * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
  6.  * All rights reserved.
  7.  *
  8.  * This software is the confidential and proprietary information
  9.  * of Sun Microsystems, Inc. ("Confidential Information").  You
  10.  * shall not disclose such Confidential Information and shall use
  11.  * it only in accordance with the terms of the license agreement
  12.  * you entered into with Sun.
  13.  */
  14. package javax.swing;
  15.  
  16.  
  17. import java.util.*;
  18. import java.awt.*;
  19. import java.awt.event.*;
  20. import java.applet.*;
  21. import java.beans.*;
  22. import javax.swing.event.*;
  23.  
  24. /**
  25.   * The KeyboardManager class is used to help dispatch keyboard actions for the
  26.   * WHEN_IN_FOCUSED_WINDOW style actions.  Actions with other conditions are handled
  27.   * directly in JComponent.
  28.   *
  29.   * Here's a description of the symantics of how keyboard dispatching should work
  30.   * atleast as I understand it.
  31.   *
  32.   * KeyEvents are dispatched to the focused component.  The focus manager gets first
  33.   * crack at processing this event.  If the focus manager doesn't want it, then
  34.   * the JComponent calls super.processKeyEvent() this allows listeners a chance
  35.   * to process the event.
  36.   *
  37.   * If none of the listeners "consumes" the event then the keybindings get a shot.
  38.   * This is where things start to get interesting.  First, KeyStokes defined with the
  39.   * WHEN_FOCUSED condition get a chance.  If none of these want the event, then the component
  40.   * walks though it's parents looked for actions of type WHEN_ANCESTOR_OF_FOCUSED_COMPONENT.
  41.   *
  42.   * If no one has taken it yet, then it winds up here.  We then look for components registered
  43.   * for WHEN_IN_FOCUSED_WINDOW events and fire to them.  Note that if none of those are found
  44.   * then we pass the event to the menubars and let them have a crack at it.  They're handled differently.
  45.   *
  46.   * Lastly, we check if we're looking at an internal frame.  If we are and no one wanted the event
  47.   * then we move up to the InternalFrame's creator and see if anyone wants the event (and so on and so on).
  48.   * 
  49.   *
  50.   * @see JComponent#registerKeyboardAction
  51.   */
  52. class KeyboardManager {
  53.  
  54.     static KeyboardManager currentManager = new KeyboardManager();
  55.  
  56.     /**
  57.       * maps top-level containers to a sub-hashtable full of keystrokes
  58.       */
  59.     Hashtable containerMap = new Hashtable();
  60.  
  61.     /**
  62.       * Maps component/keystroke pairs to a topLevel container
  63.       * This is mainly used for fast unregister operations
  64.       */
  65.     Hashtable componentKeyStrokeMap = new Hashtable();
  66.  
  67.     public static KeyboardManager getCurrentManager() {
  68.         return currentManager;
  69.     }
  70.  
  71.     public static void setCurrentManager(KeyboardManager km) {
  72.         currentManager = km;
  73.     }
  74.  
  75.     /**
  76.       * register keystrokes here which are for the WHEN_IN_FOCUSED_WINDOW
  77.       * case.
  78.       * Other types of keystrokes will be handled by walking the heirarchy
  79.       * That simplifies some potentially hairy stuff.
  80.       */
  81.      public void registerKeyStroke(KeyStroke k, JComponent c) {
  82.          Container topContainer = getTopAncestor(c);
  83.      if (topContainer == null) {
  84.          return;
  85.      }
  86.      Hashtable keyMap = (Hashtable)containerMap.get(topContainer);
  87.  
  88.      if (keyMap ==  null) {  // lazy evaluate one
  89.          keyMap = registerNewTopContainer(topContainer);
  90.      }
  91.  
  92.      Object tmp = keyMap.get(k);
  93.      if (tmp == null) {
  94.          keyMap.put(k,c);
  95.      } else if (tmp instanceof Vector) {  // if there's a Vector there then add to it.
  96.          Vector v = (Vector)tmp;
  97.          if (!v.contains(c)) {  // only add if this keystroke isn't registered for this component
  98.              v.addElement(c);
  99.          }
  100.      } else if (tmp instanceof JComponent) {  
  101.        // if a JComponent is there then remove it and replace it with a vector
  102.        // Then add the old compoennt and the new compoent to the vector
  103.        // then insert the vector in the table
  104.        if (tmp != c) {  // this means this is already registered for this component, no need to dup
  105.            Vector v = new Vector();
  106.            v.addElement(tmp);
  107.            v.addElement(c);
  108.            keyMap.put(k, v);
  109.        } 
  110.      } else {
  111.          System.out.println("Unexpected condition in registerKeyStroke");
  112.          Thread.dumpStack();
  113.      }
  114.      
  115.      componentKeyStrokeMap.put(new ComponentKeyStrokePair(c,k), topContainer);
  116.  
  117.      }
  118.  
  119.      /**
  120.        * find the top Window or Applet
  121.        */
  122.      private static Container getTopAncestor(JComponent c) {
  123.         for(Container p = c.getParent(); p != null; p = p.getParent()) {
  124.             if (p instanceof Window || p instanceof Applet || p instanceof JInternalFrame) {
  125.                 return p;
  126.         }
  127.         }
  128.         return null;
  129.      }
  130.  
  131.      public void unregisterKeyStroke(KeyStroke ks, JComponent c) {
  132.  
  133.        // component may have already been removed from the heirarchy, we
  134.        // need to look up the container using the componentKeyStrokeMap.
  135.  
  136.          ComponentKeyStrokePair ckp = new ComponentKeyStrokePair(c,ks);
  137.      
  138.      Object topContainer = componentKeyStrokeMap.get(ckp);
  139.  
  140.      if (topContainer == null) {  // never heard of this pairing, so bail
  141.          return;
  142.      }
  143.  
  144.      Hashtable keyMap = (Hashtable)containerMap.get(topContainer);
  145.      if  (keyMap == null) { // this should never happen, but I'm being safe
  146.          Thread.dumpStack();
  147.          return;
  148.      }
  149.  
  150.      Object tmp = keyMap.get(ks);
  151.      if (tmp == null) {  // this should never happen, but I'm being safe
  152.          Thread.dumpStack();
  153.          return;
  154.      }
  155.  
  156.      if (tmp instanceof JComponent && tmp == c) {
  157.          keyMap.remove(ks);  // remove the KeyStroke from the Map
  158.          //System.out.println("removed a stroke" + ks);
  159.      } else if (tmp instanceof Vector ) {  // this means there is more than one component reg for this key
  160.          Vector v = (Vector)tmp;
  161.          v.removeElement(c);
  162.          if ( v.isEmpty() ) {
  163.              keyMap.remove(ks);  // remove the KeyStroke from the Map
  164.          //System.out.println("removed a ks vector");
  165.          }
  166.      }
  167.      
  168.      if ( keyMap.isEmpty() ) {  // if no more bindings in this table
  169.          containerMap.remove(topContainer);  // remove table to enable GC
  170.          //System.out.println("removed a container");
  171.      }
  172.  
  173.      componentKeyStrokeMap.remove(ckp);
  174.      }
  175.  
  176.     /**
  177.       * This method is called when the focused component (and none of
  178.       * its ancestors) want the key event.  This will look up the keystroke
  179.       * to see if any chidren (or subchildren) of the specified container
  180.       * want a crack at the event.
  181.       * If one of them wants it, then it will "DO-THE-RIGHT-THING"
  182.       */
  183.     public boolean fireKeyboardAction(KeyEvent e, boolean pressed, Container topAncestor) {
  184.  
  185.          if (e.isConsumed()) {
  186.           System.out.println("Aquired pre-used event!");
  187.           Thread.dumpStack();
  188.          }
  189.  
  190.          KeyStroke ks;
  191.  
  192.  
  193.      if(e.getID() == KeyEvent.KEY_TYPED) {
  194.                ks=KeyStroke.getKeyStroke(e.getKeyChar());
  195.          } else {
  196.                ks=KeyStroke.getKeyStroke(e.getKeyCode(), e.getModifiers(), !pressed);
  197.      }
  198.  
  199.      Hashtable keyMap = (Hashtable)containerMap.get(topAncestor);
  200.      if (keyMap != null) { // this container isn't registered, so bail
  201.  
  202.          Object tmp = keyMap.get(ks);
  203.  
  204.          if (tmp == null) {
  205.            // don't do anything
  206.          } else if ( tmp instanceof JComponent) {
  207.              JComponent c = (JComponent)tmp;
  208.          if ( c.isShowing() && c.isEnabled() ) { // only give it out if enabled and visible
  209.              fireBinding(c, ks, e);
  210.          }
  211.          } else if ( tmp instanceof Vector) { //more than one comp registered for this
  212.              Vector v = (Vector)tmp;
  213.          Enumeration iter = v.elements();
  214.          while (iter.hasMoreElements()) {
  215.              JComponent c = (JComponent)iter.nextElement();
  216.              //System.out.println("Trying collision: " + c + " vector = "+ v.size());
  217.              if ( c.isShowing() && c.isEnabled() ) { // don't want to give these out
  218.                  fireBinding(c, ks, e);
  219.              if (e.isConsumed())
  220.                  return true;
  221.              }
  222.          }
  223.          } else  {
  224.              System.out.println( "Unexpected condition in fireKeyboardAction " + tmp);
  225.          // This means that tmp wasn't null, a JComponent, or a Vector.  What is it?
  226.          Thread.dumpStack();
  227.          }
  228.      }
  229.  
  230.      if (e.isConsumed()) {
  231.          return true;
  232.      }
  233.      // if no one else handled it, then give the menus a crack
  234.      // The're handled differently.  The key is to let any JMenuBars 
  235.      // process the event
  236.      if ( keyMap != null) {     
  237.          Vector v = (Vector)keyMap.get(JMenuBar.class);
  238.          if (v != null) {
  239.              Enumeration iter = v.elements();
  240.          while (iter.hasMoreElements()) {
  241.              JMenuBar mb = (JMenuBar)iter.nextElement();
  242.              if ( mb.isShowing() && mb.isEnabled() ) { // don't want to give these out
  243.                  fireBinding(mb, ks, e);
  244.              if (e.isConsumed()) {
  245.                  return true;
  246.              }
  247.              }
  248.          }
  249.          }
  250.      }
  251.  
  252.      // if no one has handled it yet, and the container we're working in is
  253.      // an internal frame, then move on up to it's top container.
  254.      if (topAncestor instanceof JInternalFrame) {
  255.          Container newTopContainer = getTopAncestor((JInternalFrame)topAncestor);
  256.          if (newTopContainer == null) {  
  257.            return false; // this case can occur in rare cases where a key action removes
  258.                          // the internal frame from the heirarchy.
  259.          }
  260.          fireKeyboardAction( e, pressed, newTopContainer );
  261.      }
  262.     
  263.      return e.isConsumed();
  264.     }
  265.  
  266.     void fireBinding(JComponent c, KeyStroke ks, KeyEvent e) {
  267.       //System.out.println("Firing on: " + c);
  268.      JComponent.KeyboardBinding binding = c.bindingForKeyStroke(ks, JComponent.WHEN_IN_FOCUSED_WINDOW);
  269.      if(binding != null) {  // this block of code stolen from JComponent.processKeyBinding
  270.          ActionListener listener = binding.getAction();
  271.          if(listener != null) {
  272.            
  273.              if (listener instanceof Action && ((Action)listener).isEnabled() == false) {
  274.            // this case handles when we try to dispatch to a disbled action
  275.            // instead of sending the event we return, thus giving a chance to
  276.            // other components registered for this stroke.
  277.              return;
  278.          }
  279.              listener.actionPerformed(new ActionEvent(c,ActionEvent.ACTION_PERFORMED,binding.getCommand()));
  280.          e.consume();
  281.          }
  282.      } else {
  283.        // System.out.println("Binding NULL");
  284.      }
  285.     }
  286.  
  287.     public void registerMenuBar(JMenuBar mb) {
  288.         Container top = getTopAncestor(mb);
  289.  
  290.     Hashtable keyMap = (Hashtable)containerMap.get(top);
  291.  
  292.  
  293.     if (keyMap ==  null) {  // lazy evaluate one
  294.          keyMap = registerNewTopContainer(top);
  295.     }
  296.  
  297.     Vector menuBars = (Vector)keyMap.get(JMenuBar.class); // use the menubar class as the key
  298.     if (menuBars == null) {  // if we don't have a list of menubars, then make one.
  299.         menuBars = new Vector();
  300.         keyMap.put(JMenuBar.class, menuBars);
  301.     }
  302.     menuBars.addElement(mb);
  303.     //    System.out.println("registered a menubar");
  304.     }
  305.  
  306.     public void unregisterMenuBar(JMenuBar mb) {
  307.  
  308.      Hashtable keyMap = null;
  309.      Enumeration iter = containerMap.elements();
  310.  
  311.      while(iter.hasMoreElements()) {
  312.          keyMap = (Hashtable)iter.nextElement();
  313.          Vector v = (Vector)keyMap.get(JMenuBar.class);
  314.          if (v != null) {
  315.              v.removeElement(mb);
  316.          }
  317.      }
  318.     }
  319.  
  320.     protected Hashtable registerNewTopContainer(Container topContainer) {
  321.          Hashtable keyMap = new Hashtable();
  322.          containerMap.put(topContainer, keyMap);
  323.          return keyMap;
  324.     }
  325.  
  326.     /**
  327.       * This class is used to create keys for a hashtable
  328.       * which looks up topContainers based on component, keystroke pairs
  329.       * This is used to make unregistering KeyStrokes fast
  330.       */
  331.     class ComponentKeyStrokePair {
  332.         Object component;
  333.         Object keyStroke;
  334.         
  335.         public ComponentKeyStrokePair(Object comp, Object key) {
  336.         component = comp;
  337.         keyStroke = key;
  338.     }
  339.  
  340.         public boolean equals(Object o) {
  341.         if ( !(o instanceof ComponentKeyStrokePair)) {
  342.             return false;
  343.         }
  344.         ComponentKeyStrokePair ckp = (ComponentKeyStrokePair)o;
  345.         return ((component.equals(ckp.component)) && (keyStroke.equals(ckp.keyStroke)));
  346.     }
  347.  
  348.         public int hashCode() {
  349.         return component.hashCode() * keyStroke.hashCode();
  350.     }
  351.  
  352.     }
  353.  
  354. } // end KeyboardManager
  355.